#include "StdAfx.h"
#include "EAXPresetMgr.h"

#define SOUNDSYSTEM_USE_FMODEX400

#define ADDNODE(_type, _name, _min, _max, _value) \
	pPropNode=XmlHelpers::CreateXmlNode(#_name); \
	pPropNode->setAttr("type", #_type); \
	pPropNode->setAttr("min", #_min); \
	pPropNode->setAttr("max", #_max); \
	pPropNode->setAttr("value", #_value); \
	m_pParamTemplateNode->addChild(pPropNode);

//////////////////////////////////////////////////////////////////////////
CEAXPresetMgr::CEAXPresetMgr()
{
	m_sPresetSelected         = "";
	m_nSoundID                = INVALID_SOUNDID;
	m_fReverbWetVal           = 0.0f;
	m_fReverbWetValueDefault	= 0.0f;
}

//////////////////////////////////////////////////////////////////////////
void CEAXPresetMgr::InitNodes()
{
	if (!m_pRootNode)
	{
		XmlNodeRef pPropNode;
		m_pParamTemplateNode = XmlHelpers::CreateXmlNode("Parameters");
		
		// Values below are FMOD defaults.
		ADDNODE(int,   nMasterLevel,      -100,  0,       -10);    // Master level
		ADDNODE(float, fDecayTime,        100.0, 20000.0, 1490.0); // Decay time
		ADDNODE(float, fHFDecayRatio,     0.1,   2.0,     0.83);   // HF decay ratio
		ADDNODE(float, fPreDelay,         0.0,   300.0,   7.0);    // Pre delay
		ADDNODE(float, fLateDelay,        0.0,   100.0,   11.0);   // Late delay
		ADDNODE(int,   nEarlyReflections, -100,  10,      -26.02); // Early reflections
		ADDNODE(int,   nLateReflections,  -100,  20,      2);      // Late reflections
		ADDNODE(float, fDiffusion,        0.0,   100.0,   100.0);  // Diffusion
		ADDNODE(float, fDensity,          0.0,   100.0,   100.0);  // Density
		ADDNODE(int,   nHFGain,           -100,  0,       -1);     // HF gain
		ADDNODE(int,   nLFGain,           -100,  0,       0);      // LF gain
		ADDNODE(float, fHFCrossover,      20.0,  20000.0, 5000.0); // HF crossover
		ADDNODE(float, fLFCrossover,      20.0,  1000.0,  250.0);  // LF crossover

		XmlNodeRef pNode=XmlHelpers::CreateXmlNode("EchoDSP");

		// Delay
		pPropNode = XmlHelpers::CreateXmlNode("fDelay");
		pPropNode->setAttr("type", "float");
		pPropNode->setAttr("min", 10.0f);
		pPropNode->setAttr("max", 5000.0f);
		pPropNode->setAttr("value", "500");
		pNode->addChild(pPropNode);

		// Decay Ratio
		pPropNode = XmlHelpers::CreateXmlNode("fDecayRatio");
		pPropNode->setAttr("type", "float");
		pPropNode->setAttr("min", 0.0f);
		pPropNode->setAttr("max", 1.0f);
		pPropNode->setAttr("value", "0.5");
		pNode->addChild(pPropNode);

		// DryMix
		pPropNode = XmlHelpers::CreateXmlNode("fDryMix");
		pPropNode->setAttr("type", "float");
		pPropNode->setAttr("min", 0.0f);
		pPropNode->setAttr("max", 1.0f);
		pPropNode->setAttr("value", "1");
		pNode->addChild(pPropNode);

		// WetMix
		pPropNode = XmlHelpers::CreateXmlNode("fWetMix");
		pPropNode->setAttr("type", "float");
		pPropNode->setAttr("min", 0.0f);
		pPropNode->setAttr("max", 1.0f);
		pPropNode->setAttr("value", "0");
		pNode->addChild(pPropNode);
		m_pParamTemplateNode->addChild(pNode);

		m_pRootNode=XmlHelpers::CreateXmlNode("ReverbPresetDB");

		SetPreviewSound("");
	}
}

//////////////////////////////////////////////////////////////////////////
CEAXPresetMgr::~CEAXPresetMgr()
{
	assert(m_nSoundID == INVALID_SOUNDID);
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::AddPreset(CString sName)
{
	if (sName.GetLength()<=0)
		return false;
	// check if name is valid
	for (int i=0;i<sName.GetLength();i++)
	{
		char c=sName[i];
		if (!i)
		{
			if (!isalpha(c))
				return false;
		}else
		{
			if (!isalnum(c) && c!='_' && c!='-')
				return false;
		}
	}
	if (m_pRootNode->findChild(sName))
		return false;
	XmlNodeRef pPresetNode=m_pParamTemplateNode->clone();//CreateXmlNode(sName);
	pPresetNode->setTag(sName);
	//	pPresetNode->addChild(m_pParamTemplateNode->clone());
	m_pRootNode->addChild(pPresetNode);
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::DelPreset(CString sName)
{
	XmlNodeRef pPresetNode=m_pRootNode->findChild(sName);
	if (!pPresetNode)
		return false;
	m_pRootNode->removeChild(pPresetNode);
	Reload();
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::PreviewPreset()
{
	// already previewing a preset?
	_smart_ptr<ISound> pSound;

	if (m_nSoundID != INVALID_SOUNDID)
	{
		pSound = gEnv->pSoundSystem->GetSound(m_nSoundID);

		if (pSound)
		{
			pSound->Stop(ESoundStopMode_AtOnce);
		}

		m_nSoundID = INVALID_SOUNDID;
	}

	// This will update the currently set reverb preset so potential property changes done to it will get sent to the reverb manager beforehand.
	assert(!m_sPresetSelected.empty());
	RegisterPreset(m_sPresetSelected);

	pSound = gEnv->pSoundSystem->CreateSound(m_sSound, FLAG_SOUND_3D|FLAG_SOUND_RELATIVE|FLAG_SOUND_LOAD_SYNCHRONOUSLY);

	//  play Sound and wait
	if(pSound)		
	{
		m_nSoundID = pSound->GetId();

		// Try and load the sound in case it's not loaded yet
		if(!pSound->IsLoaded())
			pSound->GetInterfaceExtended()->Preload();

		if(pSound->IsLoaded())
		{
			// Trick to prevent SoundSystem from updating the "distance" parameter.
			pSound->GetInterfaceExtended()->SetFlags(pSound->GetFlags() & ~FLAG_SOUND_RADIUS);
			pSound->SetSemantic(eSoundSemantic_Sandbox_Browser);
			pSound->AddEventListener(this, "CEAXPresetMgr");
			pSound->Play();

			ptParamF32 ParamF32(m_fReverbWetVal);
			pSound->SetParam(spREVERBWET, &ParamF32);

			return true;
		}
		else
		{
			if (!m_sPresetSelected.empty())
			{
				gEnv->pSoundSystem->GetIReverbManager()->UnregisterWeightedReverbEnvironment(m_sPresetSelected);
				m_sPresetSelected = "";
			}

			m_nSoundID = INVALID_SOUNDID;
		}
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::Reload(CString sFilename)
{
	if (!Save() || !Load())
	{
		AfxMessageBox("An error occurred while reloading sound-presets. Is Database read-only ?", MB_ICONEXCLAMATION | MB_OK);
		return false;
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::DumpTableRecursive(FILE *pFile, XmlNodeRef pNode, int nTabs)
{
	for (int i=0;i<nTabs;i++) if (fprintf(pFile, "\t")<=0) return false;
	CString sValue;
	if (pNode->getAttr("value", sValue))
	{
		for (int i=0;i<sValue.GetLength();i++)
		{
			if (sValue[i]=='\\')
				sValue.SetAt(i, '/');
		}
		CString sType;
		pNode->getAttr("type", sType);
		int res;
		if(!stricmp(sType,"int") || !stricmp(sType,"float") || !stricmp(sType,"bool"))
			res = fprintf(pFile, "%s = %s", pNode->getTag(), (const char*)sValue);
		else
			res = fprintf(pFile, "%s = \"%s\"", pNode->getTag(), (const char*)sValue);
		if (res<=0) return false;
	}else
	{
		if (fprintf(pFile, "%s = {\r\n", pNode->getTag())<=0) return false;
		for (int i=0;i<pNode->getChildCount();i++)
		{
			XmlNodeRef pChild=pNode->getChild(i);
			if (!DumpTableRecursive(pFile, pChild, nTabs+1)) return false;
		}
		for (int i=0;i<nTabs;i++) if (fprintf(pFile, "\t")<=0) return false;
		if (fprintf(pFile, "}")<=0) return false;
	}
	if (nTabs)
		if (fprintf(pFile, ",\r\n")<=0) return false;
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::Save(CString sFilename)
{

	FILE *pFile=fopen( CString(Path::GetGameFolder())+"/"+sFilename, "wb");
	if (!pFile)
		return false;
	bool bRes=true;
	bRes=DumpTableRecursive(pFile, m_pRootNode);
	fclose(pFile);
	//return bRes;

	string sFullFileName = "/";
	sFullFileName += REVERB_PRESETS_FILENAME_XML;
	sFullFileName = PathUtil::GetGameFolder() + sFullFileName;
	bool bResult = XmlHelpers::SaveXmlNode( m_pRootNode, sFullFileName.c_str());

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::Load(CString sFilename)
{
	InitNodes();

	ISystem *pSystem=GetIEditor()->GetSystem();
	if (!pSystem)
		return false;
	IScriptSystem *pScriptSystem=pSystem->GetIScriptSystem();
	if (!pScriptSystem)
		return false;
	pScriptSystem->SetGlobalToNull("ReverbPresetDB");
	pScriptSystem->ExecuteFile(sFilename, true, true);
	SmartScriptTable pObj(pScriptSystem, true);
	if (!pScriptSystem->GetGlobalValue("ReverbPresetDB", pObj))
		return false;
	m_pRootNode=XmlHelpers::CreateXmlNode("ReverbPresetDB");
	CEAXPresetSODump Sink(pScriptSystem, pObj, m_pRootNode, m_pParamTemplateNode);
	pObj->Dump(&Sink);
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::UpdateParameter(XmlNodeRef pNode)
{
	CString sPresetName;
	CString sSoundName;
	CString sPropName;
	CString sPropValue;
	XmlNodeRef pParent=pNode->getParent();

	if (!pParent)
		return false;

	sPropName=pNode->getTag();
	
	if (!pNode->getAttr("value", sPropValue))
		return false;

	IScriptSystem *pScriptSystem=GetIEditor()->GetSystem()->GetIScriptSystem();
	SmartScriptTable pObj(pScriptSystem, true);

	if (!pScriptSystem->GetGlobalValue("ReverbPresetDB", pObj))
		return false;

	sPresetName=pParent->getTag();

	CEAXPresetUpdateSODump Sink(pScriptSystem, pObj, sPresetName, sPropName, sPropValue);
	pObj->Dump(&Sink);

	CallOnPropertyChange(sPresetName);

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CEAXPresetMgr::SetPreviewSound(char const* const sSound)
{
	StopPreviewSound();
	
	if (sSound && sSound[0])
	{
		m_sSound = sSound;
	}
	else
	{
		m_sSound = "Editor/Sounds/reverb_preview.wav";
	}
}

//////////////////////////////////////////////////////////////////////////
float CEAXPresetMgr::GetPreviewSoundWetVal() const
{
	ptParamF32 ParamF32(0.0f);
	_smart_ptr<ISound> pSound;

	if (m_nSoundID == INVALID_SOUNDID)
	{
		pSound = gEnv->pSoundSystem->CreateSound(m_sSound, FLAG_SOUND_3D|FLAG_SOUND_RELATIVE|FLAG_SOUND_LOAD_SYNCHRONOUSLY);
	}
	else
	{
		pSound = gEnv->pSoundSystem->GetSound(m_nSoundID);
	}
		

	if (pSound)
	{
		// Trick to prevent SoundSystem from updating the "distance" parameter.
		pSound->GetInterfaceExtended()->SetFlags(pSound->GetFlags() & ~FLAG_SOUND_RADIUS);
		pSound->SetSemantic(eSoundSemantic_Sandbox_Browser);
		pSound->Play();
		pSound->GetParam(spREVERBWET, &ParamF32);
		pSound->Stop(ESoundStopMode_AtOnce);
	}
	else
	{
		assert(0);
	}

	float fValue;
	ParamF32.GetValue(fValue);
	return fValue;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::RegisterPreset(char const* const sPresetName)
{
	XmlNodeRef const pNodePreset(m_pRootNode->findChild(sPresetName));
	
	if (pNodePreset != NULL)
	{
		SReverbInfo::SReverbPreset oReverbPreset;
		oReverbPreset.sPresetName = sPresetName;
		XmlNodeRef pNodeParameter = NULL;

		// The values below need to be translated to match the FMOD internal scale.
		int nValue   = 0;
		float fValue = 0.0f;

		pNodeParameter = pNodePreset->findChild("nMasterLevel");
		pNodeParameter->getAttr("Value", nValue);
		oReverbPreset.EAX.nRoom = nValue * 100;
		pNodeParameter = pNodePreset->findChild("fDecayTime");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fDecayTime = fValue / 1000.0f;
		pNodeParameter = pNodePreset->findChild("fHFDecayRatio");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fDecayHFRatio = fValue;
		pNodeParameter = pNodePreset->findChild("fPreDelay");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fReflectionsDelay = fValue / 1000.0f;
		pNodeParameter = pNodePreset->findChild("fLateDelay");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fReverbDelay = fValue / 1000.0f;
		pNodeParameter = pNodePreset->findChild("nEarlyReflections");
		pNodeParameter->getAttr("Value", nValue);
		oReverbPreset.EAX.nReflections = nValue * 100;
		pNodeParameter = pNodePreset->findChild("nLateReflections");
		pNodeParameter->getAttr("Value", nValue);
		oReverbPreset.EAX.nReverb = nValue * 100;
		pNodeParameter = pNodePreset->findChild("fDiffusion");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fDiffusion = fValue;
		pNodeParameter = pNodePreset->findChild("fDensity");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fDensity = fValue;
		pNodeParameter = pNodePreset->findChild("nHFGain");
		pNodeParameter->getAttr("Value", nValue);
		oReverbPreset.EAX.nRoomHF = nValue * 100;
		pNodeParameter = pNodePreset->findChild("nLFGain");
		pNodeParameter->getAttr("Value", nValue);
		oReverbPreset.EAX.nRoomLF = nValue * 100;
		pNodeParameter = pNodePreset->findChild("fHFCrossover");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fHFReference = fValue;
		pNodeParameter = pNodePreset->findChild("fLFCrossover");
		pNodeParameter->getAttr("Value", fValue);
		oReverbPreset.EAX.fLFReference = fValue;

		// EchoDSP
		XmlNodeRef pEchoDSPNode = pNodePreset->findChild("EchoDSP");
		pNodeParameter = pEchoDSPNode->findChild("fDelay");
		pNodeParameter->getAttr("Value", oReverbPreset.EchoDSP.fDelay);
		pNodeParameter = pEchoDSPNode->findChild("fDecayRatio");
		pNodeParameter->getAttr("Value", oReverbPreset.EchoDSP.fDecayRatio);
		pNodeParameter = pEchoDSPNode->findChild("fDryMix");
		pNodeParameter->getAttr("Value", oReverbPreset.EchoDSP.fDryMix);
		pNodeParameter = pEchoDSPNode->findChild("fWetMix");
		pNodeParameter->getAttr("Value", oReverbPreset.EchoDSP.fWetMix);

		m_sPresetSelected = pNodePreset->getTag();

		ISoundSystem* const pSoundSystem = gEnv->pSoundSystem;

		unsigned int nRegID = 0; // special value for Editor preview
		pSoundSystem->GetIReverbManager()->RegisterReverbPreset(sPresetName, &oReverbPreset.EAX, &oReverbPreset.EchoDSP);
		pSoundSystem->GetIReverbManager()->RegisterWeightedReverbEnvironment(m_sPresetSelected, nRegID, true, 0);

		pSoundSystem->GetIReverbManager()->UpdateWeightedReverbEnvironment(m_sPresetSelected, nRegID, 1.0f);
		pSoundSystem->Update(eSoundUpdateMode_All);

		return true;
	}
	
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::UnregisterPreset(char const* const sPresetName)
{
	bool bResult = false;
	ISoundSystem *pSoundSystem = gEnv->pSoundSystem;

	if (!m_sPresetSelected.empty())
	{
		bResult = pSoundSystem->GetIReverbManager()->UnregisterWeightedReverbEnvironment(m_sPresetSelected);
		m_sPresetSelected = "";
	}

	StopPreviewSound();

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
void CEAXPresetMgr::StopPreviewSound()
{
	if (m_nSoundID != INVALID_SOUNDID)
	{
		_smart_ptr<ISound> const pSound(gEnv->pSoundSystem->GetSound(m_nSoundID));

		if (pSound != NULL)
		{
			pSound->Stop(ESoundStopMode_AtOnce);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEAXPresetMgr::OnSoundEvent(ESoundCallbackEvent eSoundEvent, ISound* pSound)
{
	if (pSound != NULL && eSoundEvent == SOUND_EVENT_ON_STOP && pSound->GetId() == m_nSoundID)
	{
		m_nSoundID = INVALID_SOUNDID;
		pSound->RemoveEventListener(this);

		// All of the below is necessary to get access to the FMOD event to set it back to its original reverb-wet value.
		assert(!m_sSound.empty());
		_smart_ptr<ISound> const pSoundTemp(gEnv->pSoundSystem->CreateSound(m_sSound, FLAG_SOUND_3D|FLAG_SOUND_RELATIVE|FLAG_SOUND_LOAD_SYNCHRONOUSLY));
		assert(pSoundTemp != NULL);

		if (pSoundTemp)
		{
			if(!pSoundTemp->IsLoaded())
			{
				pSoundTemp->GetInterfaceExtended()->Preload();
			}

			if(pSoundTemp->IsLoaded())
			{
				// Trick to prevent SoundSystem from updating the "distance" parameter.
				pSoundTemp->GetInterfaceExtended()->SetFlags(pSoundTemp->GetFlags() & ~FLAG_SOUND_RADIUS);
				pSoundTemp->SetSemantic(eSoundSemantic_Sandbox_Browser);
				pSoundTemp->Play();
				ptParamF32 ParamF32(m_fReverbWetValueDefault);
				pSoundTemp->SetParam(spREVERBWET, &ParamF32);
				pSoundTemp->Stop(ESoundStopMode_AtOnce);
			}
			else
			{
				assert(0);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEAXPresetMgr::ImportPresets()
{
#ifdef SOUNDSYSTEM_USE_FMODEX400
	// Import from FMOD reverb xml.
	CString const sFilePath(OpenDialog(true));

	if (!sFilePath.IsEmpty())
	{
		// Parse the FMOD reverb presets xml and copy the data.
		XmlNodeRef const pNodeRoot(gEnv->pSystem->GetXmlUtils()->LoadXmlFromFile(sFilePath.GetString()));

		if (pNodeRoot)
		{
			bool bCorrectTag = _stricmp(pNodeRoot->getTag(), "ReverbDefs") == 0;
			assert(bCorrectTag);

			if (bCorrectTag)
			{
				bool bPresetNeedsAdding    = false;
				XmlNodeRef pNodePresetFrom = NULL;
				XmlNodeRef pNodePresetTo   = NULL;
				size_t const nPresetsCount = static_cast<size_t>(pNodeRoot->getChildCount());

				for (size_t i = 0; i < nPresetsCount; ++i)
				{
					pNodePresetFrom = pNodeRoot->getChild(i);

					if (pNodePresetFrom)
					{
						bCorrectTag = _stricmp(pNodePresetFrom->getTag(), "Def") == 0;
						assert(bCorrectTag);

						if (bCorrectTag)
						{
							// Retrieve the reverb definition name.
							char const* sName = NULL;

							if (pNodePresetFrom->getAttr("name", &sName))
							{
								// Update an already existing preset, create a new one otherwise.
								pNodePresetTo = m_pRootNode->findChild(sName);
								bPresetNeedsAdding = pNodePresetTo == NULL;

								if (bPresetNeedsAdding)
								{
									pNodePresetTo = m_pParamTemplateNode->clone();
									pNodePresetTo->setTag(sName);
								}
								
								assert(pNodePresetTo != NULL);

								// Now sync the reverb parameters.
								TransferParameter(pNodePresetFrom, pNodePresetTo, "MasterLevel",      "nMasterLevel",      0);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "DecayTime",        "fDecayTime",        1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "HFDecayRatio",     "fHFDecayRatio",     1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "PreDelay",         "fPreDelay",         1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "LateDelay",        "fLateDelay",        1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "EarlyReflections", "nEarlyReflections", 0);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "LateReflections",  "nLateReflections",  0);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "Diffusion",        "fDiffusion",        1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "Density",          "fDensity",          1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "HFGain",           "nHFGain",           0);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "LFGain",           "nLFGain",           0);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "HFCrossover",      "fHFCrossover",      1);
								TransferParameter(pNodePresetFrom, pNodePresetTo, "LFCrossover",      "fLFCrossover",      1);

								if (bPresetNeedsAdding)
								{
									m_pRootNode->addChild(pNodePresetTo);
								}
							}
						}
					}
				}

				Reload();
			}
		}
	}
#endif // SOUNDSYSTEM_USE_FMODEX400
}

//////////////////////////////////////////////////////////////////////////
void CEAXPresetMgr::ExportPresets()
{
#ifdef SOUNDSYSTEM_USE_FMODEX400
	// Export to FMOD reverb xml.
	CString const sFilePath(OpenDialog(false));

	if (!sFilePath.IsEmpty())
	{
		// Write relevant data to the FMOD reverb presets xml.
		XmlNodeRef const pNodeRoot(gEnv->pSystem->CreateXmlNode("ReverbDefs"));

		if (pNodeRoot)
		{
			int nValue   = 0;
			float fValue = 0.0f;
			XmlNodeRef pNodePresetFrom, pNodePresetTo, pNodeParameter = NULL;
			size_t const nPresetsCount  = static_cast<size_t>(m_pRootNode->getChildCount());

			for (size_t i = 0; i < nPresetsCount; ++i)
			{
				pNodePresetFrom = m_pRootNode->getChild(i);
				pNodePresetTo   = pNodeRoot->newChild("Def");

				if (pNodePresetFrom != NULL && pNodePresetTo != NULL)
				{
					pNodePresetTo->setAttr("name", pNodePresetFrom->getTag());

					// Now create and sync the reverb parameters.
					pNodeParameter = pNodePresetFrom->findChild("nMasterLevel");
					pNodeParameter->getAttr("value", nValue);
					pNodeParameter = pNodePresetTo->newChild("MasterLevel");
					pNodeParameter->setAttr("units", "dB");
					pNodeParameter->setAttr("min", -100);
					pNodeParameter->setAttr("max", 0);
					pNodeParameter->setAttr("value", nValue);

					pNodeParameter = pNodePresetFrom->findChild("fDecayTime");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("DecayTime");
					pNodeParameter->setAttr("units", "ms");
					pNodeParameter->setAttr("min", 100.0f);
					pNodeParameter->setAttr("max", 20000.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("fHFDecayRatio");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("HFDecayRatio");
					pNodeParameter->setAttr("units", "ratio");
					pNodeParameter->setAttr("min", 0.1f);
					pNodeParameter->setAttr("max", 2.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("fPreDelay");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("PreDelay");
					pNodeParameter->setAttr("units", "ms");
					pNodeParameter->setAttr("min", 0.0f);
					pNodeParameter->setAttr("max", 300.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("fLateDelay");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("LateDelay");
					pNodeParameter->setAttr("units", "ms");
					pNodeParameter->setAttr("min", 0.0f);
					pNodeParameter->setAttr("max", 100.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("nEarlyReflections");
					pNodeParameter->getAttr("value", nValue);
					pNodeParameter = pNodePresetTo->newChild("EarlyReflections");
					pNodeParameter->setAttr("units", "dB");
					pNodeParameter->setAttr("min", -100);
					pNodeParameter->setAttr("max", 10);
					pNodeParameter->setAttr("value", nValue);

					pNodeParameter = pNodePresetFrom->findChild("nLateReflections");
					pNodeParameter->getAttr("value", nValue);
					pNodeParameter = pNodePresetTo->newChild("LateReflections");
					pNodeParameter->setAttr("units", "dB");
					pNodeParameter->setAttr("min", -100);
					pNodeParameter->setAttr("max", 20);
					pNodeParameter->setAttr("value", nValue);

					pNodeParameter = pNodePresetFrom->findChild("fDiffusion");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("Diffusion");
					pNodeParameter->setAttr("units", "percentage");
					pNodeParameter->setAttr("min", 0.0f);
					pNodeParameter->setAttr("max", 100.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("fDensity");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("Density");
					pNodeParameter->setAttr("units", "percentage");
					pNodeParameter->setAttr("min", 0.0f);
					pNodeParameter->setAttr("max", 100.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("nHFGain");
					pNodeParameter->getAttr("value", nValue);
					pNodeParameter = pNodePresetTo->newChild("HFGain");
					pNodeParameter->setAttr("units", "dB");
					pNodeParameter->setAttr("min", -100);
					pNodeParameter->setAttr("max", 0);
					pNodeParameter->setAttr("value", nValue);

					pNodeParameter = pNodePresetFrom->findChild("nLFGain");
					pNodeParameter->getAttr("value", nValue);
					pNodeParameter = pNodePresetTo->newChild("LFGain");
					pNodeParameter->setAttr("units", "dB");
					pNodeParameter->setAttr("min", -100);
					pNodeParameter->setAttr("max", 0);
					pNodeParameter->setAttr("value", nValue);

					pNodeParameter = pNodePresetFrom->findChild("fHFCrossover");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("HFCrossover");
					pNodeParameter->setAttr("units", "Hz");
					pNodeParameter->setAttr("min", 20.0f);
					pNodeParameter->setAttr("max", 20000.0f);
					pNodeParameter->setAttr("value", fValue);

					pNodeParameter = pNodePresetFrom->findChild("fLFCrossover");
					pNodeParameter->getAttr("value", fValue);
					pNodeParameter = pNodePresetTo->newChild("LFCrossover");
					pNodeParameter->setAttr("units", "Hz");
					pNodeParameter->setAttr("min", 20.0f);
					pNodeParameter->setAttr("max", 1000.0f);
					pNodeParameter->setAttr("value", fValue);
				}
			}

			pNodeRoot->saveToFile(sFilePath.GetString());
		}
	}

	Reload();
#endif // SOUNDSYSTEM_USE_FMODEX400
}

//////////////////////////////////////////////////////////////////////////
bool CEAXPresetMgr::TransferParameter(XmlNodeRef const& rNodePresetFrom, XmlNodeRef const& rNodePresetTo, char const* const sParameterNameFrom, char const* const sParameterNameTo, int unsigned const nDataType)
{
	bool bResult              = false;
	XmlNodeRef pNodeParameter = NULL;

	// Data type 0 == int and 1 == float
	switch (nDataType)
	{
	case 0:
		{
			int nValue = 0;

			pNodeParameter = rNodePresetFrom->findChild(sParameterNameFrom);

			if (pNodeParameter)
			{
				if (pNodeParameter->getAttr("value", nValue))
				{
					pNodeParameter = rNodePresetTo->findChild(sParameterNameTo);

					if (pNodeParameter)
					{
						pNodeParameter->setAttr("value", nValue);
						bResult = true;
					}
					else
					{
						assert(0);
					}
				}
				else
				{
					assert(0);
				}
			}
			else
			{
				assert(0);
			}

			break;
		}
	case 1:
		{
			float fValue = 0.0f;

			pNodeParameter = rNodePresetFrom->findChild(sParameterNameFrom);

			if (pNodeParameter)
			{
				if (pNodeParameter->getAttr("value", fValue))
				{
					pNodeParameter = rNodePresetTo->findChild(sParameterNameTo);

					if (pNodeParameter)
					{
						pNodeParameter->setAttr("value", fValue);
						bResult = true;
					}
					else
					{
						assert(0);
					}
				}
				else
				{
					assert(0);
				}
			}
			else
			{
				assert(0);
			}

			break;
		}
	default:
		{
			bResult = false;

			break;
		}
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
CString CEAXPresetMgr::OpenDialog(bool const bOpenFile)
{
	CString sFilePath;

	if (m_sLastOpenendDirectory.empty())
	{
		m_sLastOpenendDirectory = Path::AddBackslash(CString(GetIEditor()->GetMasterCDFolder())).GetString();
		m_sLastOpenendDirectory += Path::AddBackslash(Path::GetGameFolder()).GetString();
		m_sLastOpenendDirectory = Path::Make(m_sLastOpenendDirectory.c_str(), REVERB_PRESETS_FILENAME_XML, "").GetString();

		char sFullPath[_MAX_PATH] = {'\0'};
		char const* const sReturnValue = _fullpath(sFullPath, m_sLastOpenendDirectory.c_str(), _MAX_PATH);
		assert(sReturnValue != NULL);
		m_sLastOpenendDirectory = Path::GetPath(sFullPath).GetString();
	}

	CFileDialog oDialog(bOpenFile ? TRUE : FALSE, NULL, NULL, OFN_OVERWRITEPROMPT|OFN_NOCHANGEDIR|OFN_EXPLORER, "XML (*.xml)|*.xml||");
	oDialog.m_ofn.lpstrInitialDir = m_sLastOpenendDirectory.c_str();
	oDialog.m_ofn.lpstrTitle      = bOpenFile ? "Import Reverb Presets": "Export Reverb Presets";

	if (oDialog.DoModal() == IDOK)
	{
		sFilePath = oDialog.GetPathName().GetString();

		if (!bOpenFile)
		{
			// Safety measure to ensure that we got the correct extension.
			sFilePath = Path::ReplaceExtension(sFilePath, ".xml");
		}

		m_sLastOpenendDirectory = Path::GetPath(sFilePath).GetString();
	}

	return sFilePath;
}

void CEAXPresetMgr::CallOnPropertyChange( CString sPresetName )
{	
	// lets call OnPropertyChange on every entity-script which uses presets
	IEntitySystem *pEntSys=GetIEditor()->GetSystem()->GetIEntitySystem();
	if (pEntSys)
	{
		IEntityItPtr pEntIt=pEntSys->GetEntityIterator();
		pEntIt->MoveFirst();
		IEntity *pEnt;
		while (pEnt=pEntIt->Next())
		{
			IScriptTable *pScriptObject=pEnt->GetScriptTable();
			if (pScriptObject)
			{
				SmartScriptTable pObj(GetIEditor()->GetSystem()->GetIScriptSystem(), true);
				if (pScriptObject->GetValue("Properties", pObj))
				{
					const char *pszPreset;
					if (pObj->GetValue("reverbpresetReverbPreset", pszPreset))
					{
						if (strcmp(pszPreset, sPresetName)==0)
						{
							Script::CallMethod( pScriptObject,"OnPropertyChange" );
						}
					}
				}
			}
		}
	}
}